---
import type { ThemePresets } from '@astrojs/markdown-remark';
import type {
	BuiltinLanguage,
	LanguageRegistration,
	ShikiTransformer,
	SpecialLanguage,
	ThemeRegistration,
	ThemeRegistrationRaw,
} from 'shiki';
import { bundledLanguages } from 'shiki/langs';
import { getCachedHighlighter } from '../dist/core/shiki.js';
import type { HTMLAttributes } from '../types';

interface Props extends Omit<HTMLAttributes<'pre'>, 'lang'> {
	/** The code to highlight. Required. */
	code: string;
	/**
	 * The language of your code.
	 * Supports all languages listed here: https://shiki.style/languages
	 * Instructions for loading a custom language: https://shiki.style/guide/load-lang
	 *
	 * @default "plaintext"
	 */
	lang?: BuiltinLanguage | SpecialLanguage | LanguageRegistration;
	/**
	 * A metastring to pass to the highlighter.
	 * Allows passing information to transformers: https://shiki.style/guide/transformers#meta
	 *
	 * @default undefined
	 */
	meta?: string;
	/**
	 * The styling theme.
	 * Supports all themes listed here: https://shiki.style/themes
	 * Instructions for loading a custom theme: https://shiki.style/guide/load-theme
	 *
	 * @default "github-dark"
	 */
	theme?: ThemePresets | ThemeRegistration | ThemeRegistrationRaw;
	/**
	 * Multiple themes to style with -- alternative to "theme" option.
	 * Supports all themes found above; see https://shiki.style/guide/dual-themes for more information.
	 */
	themes?: Record<string, ThemePresets | ThemeRegistration | ThemeRegistrationRaw>;
	/**
	 * Chooses a theme from the "themes" option that you've defined as the default styling theme.
	 *  - <string>: one of the keys defined in the "themes" option. Will throw an error if the key is not defined.
	 *  - false: disabled. You'll have to apply the styling theme yourself. No effect if the "themes" option is not set.
	 *
	 * See https://shiki.style/guide/dual-themes#without-default-color for more information.
	 *
	 * @default "light"
	 */
	defaultColor?: 'light' | 'dark' | string | false;
	/**
	 * Enable word wrapping.
	 *  - true: enabled.
	 *  - false: disabled.
	 *  - null: All overflow styling removed. Code will overflow the element by default.
	 *
	 * @default false
	 */
	wrap?: boolean | null;
	/**
	 * Generate inline code element only, without the pre element wrapper.
	 *
	 * @default false
	 */
	inline?: boolean;
	/**
	 * Shiki transformers to customize the generated HTML by manipulating the hast tree.
	 * Supports all transformers listed here: https://shiki.style/packages/transformers#transformers
	 * Instructions for custom transformers: https://shiki.style/guide/transformers
	 */
	transformers?: ShikiTransformer[];
}

const {
	code,
	lang = 'plaintext',
	meta,
	theme = 'github-dark',
	themes = {},
	defaultColor = 'light',
	wrap = false,
	inline = false,
	transformers = [],
	...rest
} = Astro.props;

// shiki 1.0 compat
if (typeof lang === 'object') {
	// `id` renamed to `name` (always override)
	if ((lang as any).id) {
		lang.name = (lang as any).id;
	}
	// `grammar` flattened to lang itself
	if ((lang as any).grammar) {
		Object.assign(lang, (lang as any).grammar);
	}
}

const highlighter = await getCachedHighlighter({
	langs: [
		typeof lang === 'string'
			? Object.keys(bundledLanguages).includes(lang)
				? lang
				: 'plaintext'
			: (lang as any),
	],
	theme,
	themes,
	defaultColor,
	wrap,
	transformers,
});

const html = await highlighter.highlight(code, typeof lang === 'string' ? lang : lang.name, {
	inline,
	meta,
	attributes: rest as any,
});
---

<Fragment set:html={html} />
